﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using GurLoader;
using GurLoader.Preferences;
using GurLoader.Service;
using GurLoader.Service.ImgurTaskBuilder;
using GurLoader.Service.ScreenshotUpload;
using GurLoader.Service.TaskExecution;
using GurLoader.Util;
using GurLoader.View;
using GurLoader.ViewModel;
using Moq;
using NUnit.Framework;

namespace GurLoaderWPF.Test.Service.ScreenshotUpload
{
    [TestFixture]
    public class ScreenshotUploadServiceTests
    {
        [Test]
        public void CreateInstanceTest()
        {
            Mock<ITaskBuilderService> mockTaskBuilderService = new Mock<ITaskBuilderService>();
            Mock<ITaskExecutionService> mockTaskExecutionService = new Mock<ITaskExecutionService>();
            Mock<IViewBuilder> mockViewBuilder = new Mock<IViewBuilder>();
            Mock<IDispatcher> mockDispatcher = new Mock<IDispatcher>();
            Mock<ICommandManager> mockCommandManager = new Mock<ICommandManager>();
            GurLoaderPreferences preferences = new GurLoaderPreferences();
            
            var ssUploadService = new ScreenshotUploadService(mockTaskBuilderService.Object,
                mockTaskExecutionService.Object,
                mockViewBuilder.Object,
                mockDispatcher.Object,
                mockCommandManager.Object,
                preferences);

            Assert.IsNotNull(ssUploadService);
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void CreateInstanceNullTaskBuilderTest()
        {
            Mock<ITaskExecutionService> mockTaskExecutionService = new Mock<ITaskExecutionService>();
            Mock<IViewBuilder> mockViewBuilder = new Mock<IViewBuilder>();
            Mock<IDispatcher> mockDispatcher = new Mock<IDispatcher>();
            Mock<ICommandManager> mockCommandManager = new Mock<ICommandManager>();
            GurLoaderPreferences preferences = new GurLoaderPreferences();

            var ssUploadService = new ScreenshotUploadService(null, mockTaskExecutionService.Object, mockViewBuilder.Object, mockDispatcher.Object, mockCommandManager.Object, preferences);

            Assert.IsNotNull(ssUploadService);
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void CreateInstanceNullTaskExecutorTest()
        {
            Mock<ITaskBuilderService> mockTaskBuilderService = new Mock<ITaskBuilderService>();
            Mock<IViewBuilder> mockViewBuilder = new Mock<IViewBuilder>();
            Mock<IDispatcher> mockDispatcher = new Mock<IDispatcher>();
            Mock<ICommandManager> mockCommandManager = new Mock<ICommandManager>();
            GurLoaderPreferences preferences = new GurLoaderPreferences();

            var ssUploadService = new ScreenshotUploadService(mockTaskBuilderService.Object, null, mockViewBuilder.Object, mockDispatcher.Object, mockCommandManager.Object, preferences);
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void CreateInstanceNullViewBuilderTest()
        {
            Mock<ITaskBuilderService> mockTaskBuilderService = new Mock<ITaskBuilderService>();
            Mock<ITaskExecutionService> mockTaskExecutionService = new Mock<ITaskExecutionService>();
            Mock<IDispatcher> mockDispatcher = new Mock<IDispatcher>();
            Mock<ICommandManager> mockCommandManager = new Mock<ICommandManager>();
            GurLoaderPreferences preferences = new GurLoaderPreferences();

            var ssUploadService = new ScreenshotUploadService(mockTaskBuilderService.Object, mockTaskExecutionService.Object, null, mockDispatcher.Object, mockCommandManager.Object, preferences);
        }

        [Test]
        [ExpectedException(typeof (ArgumentNullException))]
        public void CreateInstanceNullDispatcherTest()
        {
            Mock<ITaskBuilderService> mockTaskBuilderService = new Mock<ITaskBuilderService>();
            Mock<ITaskExecutionService> mockTaskExecutionService = new Mock<ITaskExecutionService>();
            Mock<IViewBuilder> mockViewBuilder = new Mock<IViewBuilder>();
            Mock<ICommandManager> mockCommandManager = new Mock<ICommandManager>();
            GurLoaderPreferences preferences = new GurLoaderPreferences();

            var ssUploadService = new ScreenshotUploadService(mockTaskBuilderService.Object,
                mockTaskExecutionService.Object,
                mockViewBuilder.Object,
                null, 
                mockCommandManager.Object,
                preferences);
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void CreateInstanceNullCommandManagerTest()
        {
            Mock<ITaskBuilderService> mockTaskBuilderService = new Mock<ITaskBuilderService>();
            Mock<ITaskExecutionService> mockTaskExecutionService = new Mock<ITaskExecutionService>();
            Mock<IViewBuilder> mockViewBuilder = new Mock<IViewBuilder>();
            Mock<IDispatcher> mockDispatcher = new Mock<IDispatcher>();
            GurLoaderPreferences preferences = new GurLoaderPreferences();

            var ssUploadService = new ScreenshotUploadService(mockTaskBuilderService.Object,
                mockTaskExecutionService.Object,
                mockViewBuilder.Object,
                mockDispatcher.Object,
                null,
                preferences);
        }

        [Test]
        public void UploadFullScreenTest()
        {
            Mock<ITaskBuilderService> mockTaskBuilderService = new Mock<ITaskBuilderService>();
            Mock<ITaskExecutionService> mockTaskExecutionService = new Mock<ITaskExecutionService>();
            Mock<IViewBuilder> mockViewBuilder = new Mock<IViewBuilder>();
            Mock<IDispatcher> mockDispatcher = new Mock<IDispatcher>();
            Mock<ICommandManager> mockCommandManager = new Mock<ICommandManager>();
            GurLoaderPreferences preferences = new GurLoaderPreferences();

            var ssUploadService = new ScreenshotUploadService(mockTaskBuilderService.Object,
                mockTaskExecutionService.Object,
                mockViewBuilder.Object,
                mockDispatcher.Object,
                mockCommandManager.Object, preferences);

            Assert.IsNotNull(ssUploadService);

            Task<ImgurProcessResult> task = new Task<ImgurProcessResult>(() => null);

            mockTaskBuilderService.Setup(x=>x.UploadImages(
                It.IsAny<IEnumerable<ImageContent>>(), It.IsAny<Progress<IDelegatingCommandProgress>>(),
                It.IsAny<CancellationToken>(), true, mockDispatcher.Object, It.IsAny<bool>() )).Returns(task).Verifiable();

            mockTaskExecutionService.Setup(x=>x.ExecuteTask(task, 
                It.IsAny<Progress<IDelegatingCommandProgress>>(), It.IsAny<CancellationTokenSource>(), mockCommandManager.Object)).Verifiable();

            ssUploadService.UploadFullScreen();

            mockTaskBuilderService.Verify(x => x.UploadImages(
                It.IsAny<IEnumerable<ImageContent>>(), It.IsAny<Progress<IDelegatingCommandProgress>>(),
                It.IsAny<CancellationToken>(), true, mockDispatcher.Object, It.IsAny<bool>()), Times.Once);

            mockTaskExecutionService.Verify(x => x.ExecuteTask(task,
                It.IsAny<Progress<IDelegatingCommandProgress>>(), It.IsAny<CancellationTokenSource>(), mockCommandManager.Object), Times.Once);

            mockTaskBuilderService.VerifyAll();
            mockTaskExecutionService.VerifyAll();
            mockViewBuilder.VerifyAll();
            mockDispatcher.VerifyAll();
            mockCommandManager.VerifyAll();
        }

        [Test]
        public void UploadScreenAreaTest()
        {
            Mock<ITaskBuilderService> mockTaskBuilderService = new Mock<ITaskBuilderService>();
            Mock<ITaskExecutionService> mockTaskExecutionService = new Mock<ITaskExecutionService>();
            Mock<IViewBuilder> mockViewBuilder = new Mock<IViewBuilder>();
            Mock<IDispatcher> mockDispatcher = new Mock<IDispatcher>();
            Mock<ICommandManager> mockCommandManager = new Mock<ICommandManager>();
            GurLoaderPreferences preferences = new GurLoaderPreferences();

            Task<ImgurProcessResult> task = new Task<ImgurProcessResult>(() => null);

            mockTaskBuilderService.Setup(x => x.UploadImages(
                It.IsAny<IEnumerable<ImageContent>>(), It.IsAny<Progress<IDelegatingCommandProgress>>(),
                It.IsAny<CancellationToken>(), true, mockDispatcher.Object, It.IsAny<bool>())).Returns(task).Verifiable();

            mockTaskExecutionService.Setup(x => x.ExecuteTask(task,
                It.IsAny<Progress<IDelegatingCommandProgress>>(), It.IsAny<CancellationTokenSource>(), mockCommandManager.Object)).Verifiable();

            var ssUploadService = new ScreenshotUploadService(mockTaskBuilderService.Object,
                mockTaskExecutionService.Object,
                mockViewBuilder.Object,
                mockDispatcher.Object,
                mockCommandManager.Object,
                preferences);

            Assert.IsNotNull(ssUploadService);

            // when the view builder's create method is called, modify the VM to look like the
            // user has selected an area.

            ManualResetEvent mre = new ManualResetEvent(false);

            mockViewBuilder.Setup(x => x.CreateCaptureAreaWindow(
                It.IsAny<CaptureAreaViewModel>(),
                It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>()))
                .Callback<CaptureAreaViewModel,int,int,int,int>((vm,t,l,w,h) =>
                {
                    vm.SelectionStart = new Point(100, 100);
                    vm.SelectionEnd = new Point(200, 200);
                    
                    vm.AreaSelectedCommand.Execute(null);
                    mre.Set();
                });

            ssUploadService.UploadScreenArea(mockDispatcher.Object, false);
            Assert.IsTrue(mre.WaitOne(1000*3), "Timeout waiting for view builder callback.");

            mockTaskBuilderService.Verify(x => x.UploadImages(
                It.IsAny<IEnumerable<ImageContent>>(), It.IsAny<Progress<IDelegatingCommandProgress>>(),
                It.IsAny<CancellationToken>(), true, mockDispatcher.Object, It.IsAny<bool>()), Times.Once);

            mockTaskExecutionService.Verify(x => x.ExecuteTask(task,
                It.IsAny<Progress<IDelegatingCommandProgress>>(), It.IsAny<CancellationTokenSource>(), mockCommandManager.Object), Times.Once);

            mockViewBuilder.Verify(x => x.CreateCaptureAreaWindow(
                It.IsAny<CaptureAreaViewModel>(),
                It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>()), Times.Once);

            mockTaskBuilderService.VerifyAll();
            mockTaskExecutionService.VerifyAll();
            mockViewBuilder.VerifyAll();
            mockDispatcher.VerifyAll();
            mockCommandManager.VerifyAll();
        }

        [Test]
        public void UploadScreenAreaWithoutSelectionTest()
        {
            Mock<ITaskBuilderService> mockTaskBuilderService = new Mock<ITaskBuilderService>();
            Mock<ITaskExecutionService> mockTaskExecutionService = new Mock<ITaskExecutionService>();
            Mock<IViewBuilder> mockViewBuilder = new Mock<IViewBuilder>();
            Mock<IDispatcher> mockDispatcher = new Mock<IDispatcher>();
            Mock<ICommandManager> mockCommandManager = new Mock<ICommandManager>();
            GurLoaderPreferences preferences = new GurLoaderPreferences();

            Task<ImgurProcessResult> task = new Task<ImgurProcessResult>(() => null);

            var ssUploadService = new ScreenshotUploadService(mockTaskBuilderService.Object,
                mockTaskExecutionService.Object,
                mockViewBuilder.Object,
                mockDispatcher.Object,
                mockCommandManager.Object,
                preferences);

            Assert.IsNotNull(ssUploadService);

            // when the view builder's create method is called, modify the VM to look like the
            // user has selected an area.

            ManualResetEvent mre = new ManualResetEvent(false);

            mockViewBuilder.Setup(x => x.CreateCaptureAreaWindow(
                It.IsAny<CaptureAreaViewModel>(),
                It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>()))
                .Callback<CaptureAreaViewModel, int, int, int, int>((vm, t, l, w, h) =>
                {
                    // no points are selected and area selected event is not called
                    mre.Set();
                });

            ssUploadService.UploadScreenArea(mockDispatcher.Object, false);
            Assert.IsTrue(mre.WaitOne(1000 * 3), "Timeout waiting for view builder callback.");

            mockTaskBuilderService.Verify(x => x.UploadImages(
                It.IsAny<IEnumerable<ImageContent>>(), It.IsAny<Progress<IDelegatingCommandProgress>>(),
                It.IsAny<CancellationToken>(), true, mockDispatcher.Object, It.IsAny<bool>()), Times.Never);

            mockTaskExecutionService.Verify(x => x.ExecuteTask(task,
                It.IsAny<Progress<IDelegatingCommandProgress>>(), It.IsAny<CancellationTokenSource>(), mockCommandManager.Object), Times.Never);

            mockViewBuilder.Verify(x => x.CreateCaptureAreaWindow(
                It.IsAny<CaptureAreaViewModel>(),
                It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>()), Times.Once);

            mockTaskBuilderService.VerifyAll();
            mockTaskExecutionService.VerifyAll();
            mockViewBuilder.VerifyAll();
            mockDispatcher.VerifyAll();
            mockCommandManager.VerifyAll();
        }
    }
}
